package com.gsbelarus.gedemin.lib.ui.fragment;

import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ProgressBar;

import com.gsbelarus.gedemin.lib.R;
import com.gsbelarus.gedemin.lib.ui.ArgumentsHelper;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Предок всех фрагментов
 * Выполняемые задачи:
 * 1. Создает View, id которого берет у класса наследника.
 * 2. Организовывает доступ к БД в новом потоке.
 * 3. Во время загрузки отображает ProgressBar.
 */
abstract public class FragmentModel extends Fragment {

    protected Context context;

    /**
     * пул на один поток, новые потоки ставятся на паузу
     */
    private static final ExecutorService executor = Executors.newFixedThreadPool(1);

    protected volatile boolean isCancel = false;
    protected volatile boolean isRunning = false;
    private boolean isReloadViewByChangingOrientation = true;
    private boolean isShowing = true;
    private boolean isContainsParams = false;
    private boolean isShowingContentWhenLoadData = false;

    private View rootView;

    /**
     * ProgressBar. Отображается во время загрузки фрагмента и данных.
     * Автоматически убирается в методе onPostExecute после чтения данных из БД.
     */
    private ProgressBar progressBar;

    private FrameLayout contentView;

    /**
     * Класс, считывающий и хранащий все возможные передаваемые параметры
     */
    public ArgumentsHelper argumentsHelper;

    /**
     * @return id View для отрисовки в фрагменте
     */
    abstract public int getViewResource();

    /**
     * Чтение данных из БД в новом потоке
     */
    abstract public void readDataBase();

    /**
     * Метод вызывается после чтения данных из БД или при смене ориентации экрана.
     * Может быть вызван до окончания выполнения потока для запроса данных из БД, поэтому следует проверять данные на null.
     * Служит для инициализации View данными
     *
     * @param rootView корневое View
     */
    abstract public void initView(View rootView);

    /**
     * Вызывается при создании View или при смене ориентации экрана.
     * Может быть переопределен в классе наследнике
     *
     * @param rootView корневое View
     */
    public void onCreateViewFragment(View rootView) {
    }

    /**
     * Если нужно переопределяется в классе наследнике, возвращает id View заголовка
     */
    public Integer getHeaderResource() {
        return null;
    }

    /**
     * Вызывается после инициализации View
     *
     * @param rootView корневое View
     */
    public void afterInitView(View rootView) {

    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        context = getActivity().getApplicationContext();
    }

    @Override
    public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        isCancel = false;
        /** Считываем все возможные параметры*/
        argumentsHelper = new ArgumentsHelper();
        isContainsParams = argumentsHelper.readParams(getArguments());

        createView(inflater, container);

        /** Запускаем фоновый поток считывание данных из БД*/
        reloadFragmentData();

        /** Возвращаем готовый view (форму)*/
        return rootView;
    }

    private void createView(LayoutInflater inflater, ViewGroup container) {
        /** Получение view из xml файла
         * (загрузка интерфейса формы)*/
        rootView = inflater.inflate(R.layout.frame_frgm, container, false);

        progressBar = (ProgressBar) rootView.findViewById(R.id.progress_bar);

        /** если есть отдельный layout для заголовка, добавляем его */
        if (getHeaderResource() != null)
            showHeaderWithShadow(inflater);

        /** добавляем основное view для отображения данных */
        contentView = (FrameLayout) rootView.findViewById(R.id.content_frame);
        contentView.addView(inflater.inflate(getViewResource(), contentView, false));

        /** Сделать что-нибудь при создании View*/
        onCreateViewFragment(rootView);

        if (isRunning)
            showProgressBar();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (!isReloadViewByChangingOrientation)
            return;

        /** при смене ориентации пересоздаем и инициализируем rootView */
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ||
                newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {

            ViewGroup viewGroup = (ViewGroup) getView();

            if (viewGroup != null) {
                LayoutInflater inflater = LayoutInflater.from(getView().getContext());
                viewGroup.removeAllViews();
                createView(inflater, viewGroup);
                viewGroup.addView(rootView);
                initView(rootView);
                afterInitView(rootView);
            }
        }
    }

    /**
     * Показывает прогрессБар, если он есть
     */
    private void showProgressBar() {
        if (progressBar != null && contentView != null) {
            progressBar.setVisibility(View.VISIBLE);
            if (!isShowingContentWhenLoadData)
                contentView.setVisibility(View.INVISIBLE);
        }
    }

    /**
     * Убираем прогрессБар, если он есть
     */
    private void hideProgressBar() {
        if (progressBar != null && contentView != null) {
            progressBar.setVisibility(View.GONE);
            contentView.setVisibility(View.VISIBLE);
        }
    }

    /**
     * Показываем header вместе с тенью
     */
    private void showHeaderWithShadow(LayoutInflater inflater) {
        FrameLayout headerView = (FrameLayout) rootView.findViewById(R.id.header_frame);
        if (headerView != null) {
            headerView.addView(inflater.inflate(getHeaderResource(), headerView, false));
            headerView.setVisibility(View.VISIBLE);
        }
        View dropShadow = rootView.findViewById(R.id.drop_shadow);
        if (dropShadow != null)
            dropShadow.setVisibility(View.VISIBLE);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        isCancel = true;
    }

    public void reloadFragmentData() {
        showProgressBar();
        isRunning = true;
        final Handler handler = new Handler();
        /** помещаем поток в очередь */
        executor.execute(new Runnable() {
            @Override
            public void run() {

                /** если фрагмент для которго грузится данные уничтожен, закрываем поток */
                if (isCancel) return;
                readDataBase();

                /** получаем доступ к UI потоку и выполняем отрисовку данных */
                handler.post(new Runnable() {
                    @Override
                    public void run() {

                        isRunning = false;
                        if (isShowing && isAdded() && !isCancel) {
                            hideProgressBar();
                            initView(rootView);
                            afterInitView(rootView);
                        }
                    }
                });
            }
        });
    }

    public void setReloadViewByChangingOrientation(boolean isReloadViewByChangingOrientation) {
        this.isReloadViewByChangingOrientation = isReloadViewByChangingOrientation;
    }

    public boolean isReloadViewByChangingOrientation() {
        return isReloadViewByChangingOrientation;
    }

    public boolean isContainsParams() {
        return isContainsParams;
    }

    public boolean isShowingContentWhenLoadData() {
        return isShowingContentWhenLoadData;
    }

    public void setShowingContentWhenLoadData(boolean isShowContentWhenLoadData) {
        this.isShowingContentWhenLoadData = isShowContentWhenLoadData;
    }
}
